package com.yifeng.repo.controller.fadada.signature;

import com.fadada.sdk.base.client.FddBaseClient;
import com.fadada.sdk.base.model.req.*;
import com.fadada.sdk.client.FddClient;
import com.fadada.sdk.extra.client.FddExtraClient;
import com.fadada.sdk.extra.model.req.PushShortUrlSmsParams;
import com.fadada.sdk.extra.model.req.QuickSignParams;
import com.fadada.sdk.model.ReturnBase;
import com.fadada.sdk.model.ReturnBaseT;
import com.fadada.sdk.utils.crypt.FddEncryptTool;
import com.fasterxml.jackson.core.type.TypeReference;
import com.gomcarter.frameworks.base.common.AssertUtils;
import com.gomcarter.frameworks.base.exception.CustomException;
import com.yifeng.repo.base.utils.codec.URLEncodeHelper;
import com.yifeng.repo.base.utils.common.BaseUtil;
import com.yifeng.repo.base.utils.converter.JacksonHelper;
import com.yifeng.repo.controller.fadada.configure.FadadaProperties;
import com.yifeng.repo.controller.fadada.constant.AccountTypeEnum;
import com.yifeng.repo.controller.fadada.signature.dto.ExtSignAutoData;
import com.yifeng.repo.controller.fadada.signature.dto.QuickSignData;
import com.yifeng.repo.controller.fadada.signature.dto.ReturnCore;
import com.yifeng.repo.controller.fadada.signature.dto.SignatureData;
import com.yifeng.repo.controller.fadada.signature.params.CompanyInfo;
import com.yifeng.repo.controller.fadada.signature.params.SignData;
import lombok.extern.slf4j.Slf4j;

import java.util.Collections;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import static com.yifeng.repo.controller.fadada.signature.dto.ReturnCore.SUCCESS_CODE;

/**
 * Created by daibing on 2024/11/22.
 */
@Slf4j
public class FadadaSignatureManager {
    protected static final String BASE = "FddBaseClient";
    protected static final String EXTRA = "FddExtraClient";
    protected final ConcurrentMap<String, FddClient> FddClientMap = new ConcurrentHashMap<>(2);
    protected final ConcurrentMap<String, String> personOpenId2CustomerId = new ConcurrentHashMap<>();
    protected final ConcurrentMap<String, String> companyOpenId2CustomerId = new ConcurrentHashMap<>();

    protected final String applicationName;
    protected final FadadaProperties properties;

    public FadadaSignatureManager(String applicationName, FadadaProperties properties) {
        this.applicationName = applicationName;
        this.properties = properties;
    }

    public void init() {
        log.info("yfcloud controller FadadaSignatureManager init success: OWNER_ID={}, OWNER_NAME={}", BaseUtil.OWNER_ID, BaseUtil.OWNER_NAME);
    }

    public void destroy() {
        log.info("yfcloud controller FadadaSignatureManager destroy success: OWNER_ID={}, OWNER_NAME={}", BaseUtil.OWNER_ID, BaseUtil.OWNER_NAME);
    }

    /**
     * 注册账号
     *
     * @param accountType 1:个人, 2:企业
     * @param openId      自定义，建议是用户在接入方的唯一标识，比如身份证id
     * @return customerId 存在则返回对应账号。否则随机生成账号并返回账号
     * <a href="https://open.fadada.com/index.html#/portal/documentCenter/EKFL2Q6X1Q/FSTZXRP0DYH0KUAF">注册账号</a>
     */
    public String registerAccount(AccountTypeEnum accountType, String openId) {
        FddBaseClient fddClient = getBaseClient();
        RegisterAccountParams params = new RegisterAccountParams();
        params.setAccountType(accountType.getCode());
        params.setOpenId(openId);
        try {
            String result = fddClient.invokeRegisterAccount(params);
            ReturnBaseT<String> obj = JacksonHelper.toObj(result, new TypeReference<ReturnBaseT<String>>() {
            });
            AssertUtils.isTrue(obj.getCode() == ReturnBase.SUCCESS.getCode(),
                    String.format("注册账号错误: accountType=%s, openId=%s, code=%s, msg=%s", accountType, openId, obj.getCode(), obj.getMsg()));
            return obj.getData();
        } catch (Exception e) {
            throw new CustomException(e);
        }
    }

    public String registerAccountByCache(AccountTypeEnum accountType, String openId) {
        switch (accountType) {
            case PERSON:
                return personOpenId2CustomerId.computeIfAbsent(openId, k -> registerAccount(accountType, openId));
            case COMPANY:
                return companyOpenId2CustomerId.computeIfAbsent(openId, k -> registerAccount(accountType, openId));
        }
        throw new CustomException("未匹配到账号类型：" + accountType + ", " + openId);
    }

    /**
     * 印章上传
     *
     * @param customerId 客户编号
     * @param imgUrl     图片公网地址
     * @return signatureId 签章图片ID
     * <a href="https://open.fadada.com/index.html#/portal/documentCenter/ZLZRHIWBZK/FRNXNT42GGE1LPSP">印章上传</a>
     */
    public String addSignature(String customerId, String imgUrl) {
        FddBaseClient fddClient = getBaseClient();
        AddSignatureParams params = new AddSignatureParams();
        params.setCustomerId(customerId);
        params.setImgUrl(imgUrl);
        try {
            String result = fddClient.invokeAddSignature(params);
            ReturnBaseT<SignatureData> obj = JacksonHelper.toObj(result, new TypeReference<ReturnBaseT<SignatureData>>() {
            });
            AssertUtils.isTrue(obj.getCode() == ReturnBase.SUCCESS.getCode(),
                    String.format("印章上传错误: customerId=%s, imgUrl=%s, code=%s, msg=%s", customerId, imgUrl, obj.getCode(), obj.getMsg()));
            return obj.getData().getSignatureId();
        } catch (Exception e) {
            throw new CustomException(e);
        }
    }

    /**
     * 合同上传
     *
     * @param contractId 合同编号
     * @param docTitle   合同标题
     * @param docUrl     文档地址
     * @see <a href="https://open.fadada.com/index.html#/portal/documentCenter/U09RVJDZKW/0YGCJ64ZVEEV2OLB">合同上传</a>
     */
    public void uploadDocs(String contractId, String docTitle, String docUrl) {
        FddBaseClient fddClient = getBaseClient();
        UploadDocsParams params = new UploadDocsParams();
        params.setContractId(contractId);
        params.setDocTitle(docTitle);
        params.setDocUrl(docUrl);
        params.setDocType(".pdf");
        try {
            String result = fddClient.invokeUploadDocs(params);
            ReturnCore obj = JacksonHelper.toObj(result, ReturnCore.class);
            AssertUtils.isTrue(obj.getCode().equals(SUCCESS_CODE),
                    String.format("合同上传错误: contractId=%s, docTitle=%s, docUrl=%s, code=%s, msg=%s",
                            contractId, docTitle, docUrl, obj.getCode(), obj.getMsg()));
        } catch (Exception e) {
            throw new CustomException(e);
        }
    }

    /**
     * 自动签署
     *
     * @param transactionId         交易号
     * @param customerId            客户编号
     * @param contractId            合同编号
     * @param docTitle              文档标题
     * @param signaturePositionCode 定位坐标code
     * @return 合同地址
     * <a href="https://open.fadada.com/index.html#/portal/documentCenter/MTKGDVFFTS/YYGDYLFKTTDVHTAZ">自动签署</a>
     */
    public ExtSignAutoData extSignAuto(String transactionId, String customerId, String contractId, String docTitle, String signaturePositionCode) {
        FddBaseClient fddClient = getBaseClient();
        ExtSignAutoParams params = new ExtSignAutoParams();
        params.setCustomerId(customerId);
        params.setContractId(contractId);
        params.setTransactionId(transactionId);
        params.setDocTitle(docTitle);
        // 定位类型: 0-关键字（默认）, 1-坐标
        params.setPositionType("1");
        // 定位坐标，JsonArray字符串 [{"pagenum":0,"x":350,"y":350},{"pagenum":1,"x":350.225,"y":750}]
        FadadaProperties.SignaturePosition signaturePosition = properties.getSignaturePositions().stream()
                .filter(p -> signaturePositionCode.equals(p.getCode()))
                .findFirst()
                .orElseThrow(() -> new CustomException("未匹配到签章位置配置：signaturePositionCode=" + signaturePositionCode));
        params.setSignaturePositions(JacksonHelper.toJson(Collections.singleton(signaturePosition)));
        // 签署结果异步通知URL，如果指定，当签章完成后，法大大将向此URL发送签署结果
        params.setNotifyUrl(properties.getCallbackPrefix() + "/ca/SignResult");
        try {
            String result = fddClient.invokeExtSignAuto(params);
            ExtSignAutoData obj = JacksonHelper.toObj(result, ExtSignAutoData.class);
            AssertUtils.isTrue(obj.getCode().equals(SUCCESS_CODE),
                    String.format("自动签署错误: transactionId=%s, customerId=%s, contractId=%s, docTitle=%s, code=%s, msg=%s",
                            transactionId, customerId, contractId, docTitle, obj.getCode(), obj.getMsg()));
            return obj;
        } catch (Exception e) {
            throw new CustomException(e);
        }
    }


    /**
     * 合同下载
     *
     * @param contractId 合同编号
     * @return docUrl 文档地址
     * <a href="https://open.fadada.com/index.html#/portal/documentCenter/1YEJOHTBV2/MEKQMHH4DUN5CE63">合同上传</a>
     */
    public String downloadPdf(String contractId) {
        FddBaseClient fddClient = getBaseClient();
        DownloadPdfParams params = new DownloadPdfParams();
        params.setContractId(contractId);
        try {
            String result = fddClient.invokeDownloadPdf(params);
            ReturnCore obj = JacksonHelper.toObj(result, ReturnCore.class);
            AssertUtils.isTrue(obj.getCode().equals(SUCCESS_CODE),
                    String.format("合同下载错误: contractId=%s, code=%s, msg=%s", contractId, obj.getCode(), obj.getMsg()));
            return result;
        } catch (Exception e) {
            throw new CustomException(e);
        }
    }


    /**
     * 合同归档
     *
     * @param contractId 合同编号
     * @return docUrl 文档地址
     * <a href="https://open.fadada.com/index.html#/portal/documentCenter/O3XZYZSR9C/XVUNBPZQWX37OLEA">合同归档</a>
     */
    public void contractFilling(String contractId) {
        FddBaseClient fddClient = getBaseClient();
        ContractFillingParams params = new ContractFillingParams();
        params.setContractId(contractId);
        try {
            String result = fddClient.invokeContractFilling(params);
            ReturnCore obj = JacksonHelper.toObj(result, ReturnCore.class);
            AssertUtils.isTrue(obj.getCode().equals(SUCCESS_CODE),
                    String.format("合同归档错误: contractId=%s, code=%s, msg=%s", contractId, obj.getCode(), obj.getMsg()));
        } catch (Exception e) {
            throw new CustomException(e);
        }
    }


    /**
     * 新批量快捷签接口（支持个人及授权自动签）
     *
     * @param transactionId   交易号
     * @param customerId      客户编号
     * @param customerName    姓名
     * @param customerIdentNo 证件号码
     * @param contractId      合同编号
     * @see <a href="https://open.fadada.com/index.html#/portal/documentCenter/ADKNI9FCLV/UF9IT4ZNOW8UAP0P">新批量快捷签接口（支持个人、企业及授权自动签）</a>
     */
    public QuickSignData personQuickSign(String transactionId, String customerId, String customerName, String customerIdentNo, String contractId) {
        FddExtraClient fddClient = getExtraClient();
        QuickSignParams params = new QuickSignParams();
        params.setCustomerId(customerId);
        params.setVerifiedWay(properties.getVerifiedWayPerson());
        //是否允许用户页面修改 1允许 2不允许
        params.setPageModify("1");
        //是否为授权自动签
        params.setAuthAutoSign("1");
        //签署内容，JsonArray字符串 [{"contractId":"cid01","signKey word":"kw01","transactionId":"t id01"}, {"contractId":"cid02","signKeyw ord":"kw02","transactionId":"tid02"}] 需要URLEncoder，编码UTF-8
        String signData = JacksonHelper.toJson(Collections.singleton(new SignData(contractId, transactionId, transactionId)));
        params.setSignData(URLEncodeHelper.encode(signData));
        // 批次号
        params.setBatchId(transactionId);
        // 批次标题
        params.setBatchTitle(transactionId);
        // 签署结果异步通知URL
        params.setNotifyUrl(properties.getCallbackPrefix() + "/ca/autoSignStatus");
        // 认证结果异步回调地址
        params.setVerifiedNotifyUrl(properties.getCallbackPrefix() + "/ca/verifyStatus");
        // 姓名 当为个人的授权自动签时必传
        params.setCustomerName(customerName);
        // 证件号码 当为个人的授权自动签时必传
        params.setCustomerIdentNo(customerIdentNo);
        // 签章类型 0：标准 1：手写 2：全部
        params.setMobileSignType("1");
        // PC支持手写签名
        params.setPcHandSignature("1");
        // 是否保存手写签名印章作为默认章
        params.setSaveHandSignature("1");
        try {
            String result = fddClient.invokeQuickSign(params);
            ReturnBaseT<QuickSignData> obj = JacksonHelper.toObj(result, new TypeReference<ReturnBaseT<QuickSignData>>() {
            });
            AssertUtils.isTrue(obj.getCode() == ReturnBase.SUCCESS.getCode(),
                    String.format("个人新批量快捷签接口错误: transactionId=%s, customerId=%s, contractId=%s, code=%s, msg=%s",
                            transactionId, customerId, contractId, obj.getCode(), obj.getMsg()));
            return obj.getData();
        } catch (Exception e) {
            throw new CustomException(e);
        }
    }

    /**
     * 新批量快捷签接口（支持企业及授权自动签）
     *
     * @param transactionId   交易号
     * @param customerId      客户编号
     * @param companyName     企业名称
     * @param companyCreditNo 统一社会信用代码
     * @param contractId      合同编号
     * @see <a href="https://open.fadada.com/index.html#/portal/documentCenter/ADKNI9FCLV/UF9IT4ZNOW8UAP0P">新批量快捷签接口（支持个人、企业及授权自动签）</a>
     */
    public QuickSignData companyQuickSign(String transactionId, String customerId, String companyName, String companyCreditNo, String contractId) {
        FddExtraClient fddClient = getExtraClient();
        QuickSignParams fddParams = new QuickSignParams();
        fddParams.setCustomerId(customerId);
        // 是否允许用户页面修改 1允许 2不允许
        fddParams.setPageModify("1");
        fddParams.setVerifiedWay(properties.getVerifiedWayCompany());
        fddParams.setMVerifiedWay(properties.getVerifiedWayAdmin());
        // 是否为授权自动签
        fddParams.setAuthAutoSign("1");
        // 签署内容，JsonArray字符串 [{"contractId":"cid01","signKey word":"kw01","transactionId":"t id01"}, {"contractId":"cid02","signKeyw ord":"kw02","transactionId":"tid02"}] 需要URLEncoder，编码UTF-8
        String signData = JacksonHelper.toJson(Collections.singleton(new SignData(contractId, transactionId, transactionId)));
        fddParams.setSignData(URLEncodeHelper.encode(signData));
        // 批次号
        fddParams.setBatchId(transactionId);
        // 批次标题
        fddParams.setBatchTitle(transactionId);
        // 签署结果异步通知URL
        fddParams.setNotifyUrl(properties.getCallbackPrefix() + "/ca/autoSignStatus");
        // 认证结果异步回调地址
        fddParams.setVerifiedNotifyUrl(properties.getCallbackPrefix() + "/ca/verifyStatus");
        // 企业名称 当为企业的授权自动签时必传
        fddParams.setCompanyInfo(JacksonHelper.toJson(new CompanyInfo(companyName, companyCreditNo)));
        // 组织类型 0：企业； 1：政府/事业单位； 2：其他组织； 3：个体工商户
        fddParams.setOrganizationType("2");
        try {
            String result = fddClient.invokeQuickSign(fddParams);
            ReturnBaseT<QuickSignData> obj = JacksonHelper.toObj(result, new TypeReference<ReturnBaseT<QuickSignData>>() {
            });
            AssertUtils.isTrue(obj.getCode() == ReturnBase.SUCCESS.getCode(),
                    String.format("企业新批量快捷签接口错误: transactionId=%s, customerId=%s, contractId=%s, code=%s, msg=%s",
                            transactionId, customerId, contractId, obj.getCode(), obj.getMsg()));
            return obj.getData();
        } catch (Exception e) {
            throw new CustomException(e);
        }
    }


    /**
     * 自定义短信发送短链接口
     *
     * @param sourceUrl 需要被转换的长地址，不超过3000位
     * @param mobile    手机号
     *                  <a href="https://open.fadada.com/index.html#/portal/documentCenter/QQ7CSE6PSX/O9VDALFJYLG6ZZ3D">自定义短信发送短链接口</a>
     */
    public void pushShortUrlSms(String sourceUrl, String mobile) {
        String encryptMobile = FddEncryptTool.encrypt(mobile, properties.getFadadaAppSecret());
        FddExtraClient fddClient = getExtraClient();
        PushShortUrlSmsParams params = new PushShortUrlSmsParams();
        params.setSourceUrl(sourceUrl);
        params.setMobile(encryptMobile);
        // 短信模板:message_type为1时候不能为空: 2：实名认证时候推送短链
        params.setSmsTemplateType("2");
        // 过期时间（默认七天后过期，单位：分钟） 360代表360分钟，不超过10080分钟
        params.setExpireTime(7 * 24 * 60);
        // 发送短信类型 1填充模板：云端填充短信模板 2自定义模板：调用方传message_content
        params.setMessageType("1");
        try {
            String result = fddClient.invokePushShortUrlSms(params);
            ReturnBaseT<String> obj = JacksonHelper.toObj(result, new TypeReference<ReturnBaseT<String>>() {
            });
            AssertUtils.isTrue(obj.getCode() == ReturnBase.SUCCESS.getCode(),
                    String.format("自定义短信发送短链接口错误: sourceUrl=%s, mobile=%s, code=%s, msg=%s", sourceUrl, mobile, obj.getCode(), obj.getMsg()));
        } catch (Exception e) {
            throw new CustomException(e);
        }
    }


    protected FddBaseClient getBaseClient() {
        return (FddBaseClient) FddClientMap.computeIfAbsent(BASE, k -> new FddBaseClient(
                properties.getFadadaAppId(),
                properties.getFadadaAppSecret(),
                properties.getVersion(),
                properties.getFadadaUrl(),
                properties.getReadTimeout(),
                properties.getWriteTimeout(),
                properties.getFileReadTimeout(),
                properties.getFileWriteTimeout()
        ));
    }

    protected FddExtraClient getExtraClient() {
        return (FddExtraClient) FddClientMap.computeIfAbsent(EXTRA, k -> new FddExtraClient(
                properties.getFadadaAppId(),
                properties.getFadadaAppSecret(),
                properties.getVersion(),
                properties.getFadadaUrl(),
                properties.getReadTimeout(),
                properties.getWriteTimeout(),
                properties.getFileReadTimeout(),
                properties.getFileWriteTimeout()
        ));
    }
}
